package org.tamal.spring;

import java.io.Serializable;
import java.util.Date;
import java.util.List;

import javax.persistence.EntityManager;
import javax.persistence.NoResultException;
import javax.persistence.PersistenceContext;
import javax.persistence.Query;
import javax.persistence.TypedQuery;

import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;
import org.tamal.Constants;
import org.tamal.persistence.User;


/**
 * This is the JPA implementation of service or business interface.
 * @author Tamal Kanti Nath
 */
@Repository
@Transactional("jpaTransactionManager")
public final class JpaService implements Service {

    @PersistenceContext
    private EntityManager entityManager;

    @Override
    @SuppressWarnings("unchecked")
    public <T> T findEntity(String query, Object... params) {
        Query q = entityManager.createQuery(query);
        for (int i = 0; i < params.length; ++i) {
            q.setParameter(i + 1, params[i]);
        }
        return (T) q.getSingleResult();
    }

    @Override
    @SuppressWarnings("unchecked")
    public <T> List<T> findEntities(String query, Object... params) {
        Query q = entityManager.createQuery(query);
        for (int i = 0; i < params.length; ++i) {
            q.setParameter(i + 1, params[i]);
        }
        return q.getResultList();
    }

    @Override
    public <T> T findEntity(Class<T> cls, String query, Object... params) {
        TypedQuery<T> typedQuery = entityManager.createQuery(query, cls);
        for (int i = 0; i < params.length; i += 2) {
            typedQuery.setParameter((String) params[i], params[i + 1]);
        }
        return typedQuery.getSingleResult();
    }

    @Override
    public <T> List<T> findEntities(Class<T> cls, String query,
            Object... params) {
        TypedQuery<T> typedQuery = entityManager.createQuery(query, cls);
        for (int i = 0; i < params.length; i += 2) {
            typedQuery.setParameter((String) params[i], params[i + 1]);
        }
        return typedQuery.getResultList();
    }

    @Override
    public void persist(Serializable entity) {
        entityManager.persist(entity);
    }

    @Override
    public <T> T merge(T entity) {
        return entityManager.merge(entity);
    }

    @Override
    public String login(User user) {
        User usr = null;
        try {
            usr = findEntity("from User where email=?1", user.getEmail());
        } catch (NoResultException e) {
            if (user.getProvider() == null) {
                return "login.invalid";
            }
            persist(user);
            return null;
        }
        if (user.getProvider() != null) {
            usr.setName(user.getName());
            usr.setProvider(user.getProvider());
            usr.setLoginAttempts(0);
            user.copy(usr);
            return null;
        }
        Date now = new Date();
        if (usr.getLoginAttempts() > Constants.MAX_LOGIN_ATTEMPT
                && now.getTime() - usr.getLastLoginFailed().getTime()
                < Constants.RETRY_INTERVAL) {
            return "login.locked";
        }
        if (!user.getPassword().equals(usr.getPassword())) {
            usr.setLastLoginFailed(new Date());
            usr.setLoginAttempts(usr.getLoginAttempts() + 1);
            return "login.invalid";
        }
        if (now.getTime() - usr.getPasswordChanged().getTime()
                > Constants.PASSWORD_EXPIRE) {
            return "login.password.expired";
        }
        usr.setLastLogin(now);
        usr.setLoginAttempts(0);
        user.copy(usr);
        return null;
    }

}
